
//#define DEBUG_TEST
/*******************************************************************************
*	HEADER FILES
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/gpio.h>

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
//#include <asm/io.h>

#ifndef DEBUG_TEST
#include <mach/io.h>
#include <mach/cputype.h>
#include <mach/hardware.h>
//#include "globaldef.h"
#endif

/*******************************************************************************
*	LOCAL DEFINES
*/

#define DEVICE_NAME "i2c_ww"
#define I2C_MAJOR 231

#ifndef DEBUG_TEST
#define __REG(addr)   (*(volatile unsigned int *)IO_ADDRESS(addr))
#define REG_PINMUX0     __REG(0x01c40000)
#define REG_PINMUX1     __REG(0x01c40004)
#define REG_PINMUX2     __REG(0x01c40008)
#define REG_PINMUX3     __REG(0x01c4000c)
#define REG_PINMUX4     __REG(0x01c40010)
#endif

#define SCK_LOOP 	100
#define BUFSIZE		32

enum I2C_CTRL_CMD
{
  I2C_SET_WAKE = 0,
  I2C_SET_START,
  I2C_SET_STOP,
  I2C_SET_SYNC
};
/*******************************************************************************
*	FILE GLOBALS
*/
static int gpio_i2c_sda       = 29;
static int gpio_i2c_scl      = 31;

static int i2c_delay_us   = 1;
static int i2c_wake_us   = 100;
static int i2c_wait_us   = 4000;

static unsigned long   irq_flags = 0;

static unsigned int i2c_major = I2C_MAJOR;
static unsigned int i2c_minor = 0;
static struct cdev i2c_dev;
unsigned char recv_buf[BUFSIZE];
unsigned char send_buf[BUFSIZE];
static struct class *i2cdev_class;

/*delay time unit: us */
void i2cDelay(unsigned int sck)
{
  udelay(sck);
}
int i2c_gpio_init(void)
{
  int status = 0;
#ifndef DEBUG_TEST
  /* request the gpios */

  status = gpio_request(gpio_i2c_sda, DEVICE_NAME);
  if(status<0)
  {    
    printk("<1>""the i2c gpio %d is busy!\n",gpio_i2c_sda);
    return status;
  }
  status = gpio_request(gpio_i2c_scl, DEVICE_NAME);
  if(status<0)
  {    
    printk("<1>""the i2c gpio %d is busy!\n",gpio_i2c_scl);
    return status;
  }

  /* gpio pins mux set */
  REG_PINMUX4 &= ~(1 << 5);  //GIO29 used as gpio
  REG_PINMUX4 &= ~(1 << 4);
  REG_PINMUX4 &= ~(1 << 9);  //GIO31 used as gpio
  REG_PINMUX4 &= ~(1 << 8);

  /* gpio direction set */
  gpio_direction_output(gpio_i2c_sda, 1);
  gpio_direction_output(gpio_i2c_scl, 1);
  //gpio_direction_input(gpio_i2c_din);

  /* gpio output set 1*/
  gpio_set_value(gpio_i2c_sda, 1);
  gpio_set_value(gpio_i2c_scl, 1);

#endif
  return status;
}
u8 i2c_gpio_start(void)
{
  gpio_set_value(gpio_i2c_scl, 1);
  i2cDelay(i2c_delay_us);
  gpio_set_value(gpio_i2c_sda, 0);
  return 0;
}
u8 i2c_gpio_stop(void)
{
  gpio_set_value(gpio_i2c_scl, 1);
  i2cDelay(i2c_delay_us);
  gpio_set_value(gpio_i2c_sda, 1);
  return 0;
}
/* -------------------------------------------------------------------------*
* Function   : i2cWrBit                                                    *
* Description: This function is used to transfer a bit to i2c bus.         *
* Parameters : None                                                        *
* Return     : None                                                        *
* -------------------------------------------------------------------------*/
u8 i2cWrBit(int vbit)
{
  u8 readbit = 0;
#ifdef DEBUG_TEST
  return 1;
#else
  //  gpio_set_value(gpio_i2c_clk, i2c_cpol);
  //  gpio_set_value(gpio_i2c_clk, (i2c_cpha^i2c_cpol));
  //  gpio_set_value(gpio_i2c_dout, vbit);
  //  i2cDelay(i2c_delay_us);
  //  gpio_set_value(gpio_i2c_clk, 1-(i2c_cpha^i2c_cpol));
  //  i2cDelay(i2c_delay_us);
  //  readbit = (gpio_get_value(gpio_i2c_din) >> (gpio_i2c_din % 32));
  //  gpio_set_value(gpio_i2c_clk, i2c_cpol);
  return readbit;
#endif
}

u8 i2c_gpio_wake(void)
{
  u8 recvData =0;
  gpio_set_value(gpio_i2c_scl, 1);
  i2cDelay(i2c_delay_us);

  gpio_set_value(gpio_i2c_sda, 0);
  i2cDelay(i2c_wake_us);

  gpio_set_value(gpio_i2c_sda, 1);
  i2cDelay(i2c_wait_us);
  //printk("<1>""toggle gpio_sda to wake\n");

  return 0;
}
u8 i2c_gpio_resync(void)
{
  int i=0;
  i2c_gpio_start();
  for(i=0;i<9;i++)
  {
    gpio_set_value(gpio_i2c_scl, 0);
    i2cDelay(i2c_delay_us);
    gpio_set_value(gpio_i2c_sda, 1);
    i2cDelay(i2c_delay_us);
    gpio_set_value(gpio_i2c_scl, 1);
    i2cDelay(i2c_delay_us);
  }
  i2c_gpio_start();
  i2c_gpio_stop();
  return 0;
}
u8 i2c_sendbyte(u8 iSendData)
{
  s8 iCount = 0;
  u8 recvData = 0;

//  printk("<1>""i2c_out:%d\n",iSendData);
  for(iCount=7; iCount>=0; iCount--)
  {
    gpio_set_value(gpio_i2c_scl, 0);
    i2cDelay(i2c_delay_us);
    gpio_set_value(gpio_i2c_sda, (iSendData>>iCount) & 1);
 // printk("<1>""i2c_out bit:%d\n",(iSendData>>iCount) & 1);
    i2cDelay(i2c_delay_us);
    gpio_set_value(gpio_i2c_scl, 1);
    i2cDelay(i2c_delay_us);
  }
  //read the ACK bit
  gpio_set_value(gpio_i2c_scl, 0);
  //gpio_direction_input(gpio_i2c_sda);
  i2cDelay(i2c_delay_us);
  gpio_set_value(gpio_i2c_scl, 1);
  i2cDelay(i2c_delay_us);
  recvData = (gpio_get_value(gpio_i2c_sda) >> (gpio_i2c_sda % 32));
//  printk("<1>""i2c_in  bit:%d\n",recvData);
  //gpio_direction_output(gpio_i2c_sda, recvData);

#ifdef DEBUG_TEST
  printk("<1>""send:0x%02x recv:0x%02x\n",iSendData,recvData);
#endif
  return recvData ;
}
u8 i2c_readbyte()
{
  s8 iCount = 0;
  u8 recvData = 0;
  for(iCount=0; iCount<8; iCount++)
  {
    gpio_set_value(gpio_i2c_scl, 0);
    i2cDelay(i2c_delay_us);
    gpio_set_value(gpio_i2c_scl, 1);
    i2cDelay(i2c_delay_us);
    recvData = (recvData<<1) | (gpio_get_value(gpio_i2c_sda) >> (gpio_i2c_sda % 32));
    i2cDelay(i2c_delay_us);
  }
  gpio_set_value(gpio_i2c_scl, 0);
  i2cDelay(i2c_delay_us);
  gpio_set_value(gpio_i2c_sda, 0);
  i2cDelay(i2c_delay_us);
  gpio_set_value(gpio_i2c_scl, 1);
  return recvData;

}

static int i2c_open(struct inode *inode, struct file *filp)
{
  /* Init the board addrss */
  int status;
  printk(KERN_ALERT "Init the gpios for i2c.\n");
  status = i2c_gpio_init();
  return status;
}
static int i2c_release(struct inode *inode, struct file *file)
{
  printk(KERN_ALERT "close the gpios for i2c.\n");
  gpio_free(gpio_i2c_sda);
  gpio_free(gpio_i2c_scl);
  return 0;
}
static ssize_t i2c_read(struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
  unsigned long		missing;
  int i = 0;
  if (count > BUFSIZE)
    return -EMSGSIZE;
  missing = copy_from_user(send_buf, buf, 1);
  if (missing !=0 )
    return -EFAULT;

  if(i2c_sendbyte(send_buf[0] | 0x01) !=0)
    return 1;

  for(i=0;i<count;i++)
    recv_buf[i] = i2c_readbyte();
  missing = copy_to_user(buf, recv_buf, count);
  if (missing !=0 )
    return -EFAULT;
  return 0;
}

static ssize_t i2c_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
  ssize_t		      status = 0;
  unsigned long		missing = 0;
  int             i = 0;

  local_irq_save(irq_flags);    // turn off all irqs;

  missing = copy_from_user(send_buf, buf, count);
  if(missing == 0)
  {
//    i2c_gpio_start();
    for(i = 0;i<count;i++)
    {
      status = i2c_sendbyte(send_buf[i]);
      if(status != 0)
      {
        printk(KERN_ALERT "ERROR: SHA204 has no response!\n");
        break;
      }
    }
//    i2c_gpio_stop();
  }

  local_irq_restore(irq_flags); //turn on all irqs;
  return status;
}
static long i2c_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
  long retval = 0;
  switch(cmd)
  {
  case I2C_SET_WAKE:
    i2c_gpio_wake();
    break;
  case I2C_SET_START:
    i2c_gpio_start();
    break;
  case I2C_SET_STOP:
    i2c_gpio_stop();
    break;
  case I2C_SET_SYNC:
    i2c_gpio_resync();
  default:
    break;
  }
  return retval;
}

static int i2c_create_dev(void)
{
  struct device *dev;
  int status;
  i2cdev_class = class_create(THIS_MODULE, DEVICE_NAME); //register a class to allow "mdev" in "/dev/"
  if (IS_ERR(i2cdev_class)) {
    return PTR_ERR(i2cdev_class);
  }

  dev = device_create(i2cdev_class,NULL, MKDEV(i2c_major, 0), NULL, DEVICE_NAME);

  status = IS_ERR(dev) ? PTR_ERR(dev) : 0;

  return status;
}
static struct file_operations i2c_fops = {
  .owner = THIS_MODULE,
  .open  = i2c_open,
  .read  = i2c_read,
  .write = i2c_write,
  .unlocked_ioctl = i2c_ioctl,
  .release = i2c_release,
};

static int __init i2c_init(void)
{
  int res;
  dev_t devon = MKDEV(i2c_major, 0);
  i2c_release(NULL,NULL);
  if(i2c_major)
    res = register_chrdev_region(devon, 1, DEVICE_NAME);
  else
    res = alloc_chrdev_region(&devon, 0, 1, DEVICE_NAME);
  if(res < 0)	{
    printk(KERN_ALERT "ERROR: i2c cannot get device number!\n");
    return res;
  }

  i2c_major = MAJOR(devon);
  i2c_minor = MINOR(devon);

  cdev_init(&i2c_dev, &i2c_fops);
  i2c_dev.owner = THIS_MODULE;
  i2c_dev.ops = &i2c_fops;

  if( cdev_add(&i2c_dev, devon, 1) < 0)	{
    printk(KERN_ALERT "Add the i2c device error\n");
    unregister_chrdev_region(devon, 1);
    return -1;
  }

  //printk(KERN_ALERT "You must create the dev file manually.\n");
  //printk(KERN_ALERT "TODO: mknod /dev/%s c %d %d\n",DEVICE_NAME, i2c_major, i2c_minor);
  if(i2c_create_dev() < 0)
  {
    printk(KERN_ALERT "can not create the i2c device file in /dev/\n");
    unregister_chrdev_region(devon, 1);
    return -1;
  }
  printk(KERN_ALERT "TODO: device file has been made: /dev/%s\n",DEVICE_NAME);

  return 0;
}

static void __exit i2c_exit(void)
{
  printk(KERN_ALERT "The i2c driver exit!\n");
  device_destroy(i2cdev_class,MKDEV(i2c_major, 0));
  class_destroy(i2cdev_class);
  cdev_del(&i2c_dev);
  unregister_chrdev_region(MKDEV(i2c_major, 0), 1);
}

module_init(i2c_init);
module_exit(i2c_exit);

MODULE_AUTHOR("WOW");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("i2c driver for gpio");
